﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Xml.Serialization;
using Microsoft.Win32;
using Parse;

namespace ParseControls
{
    public class ParseObjectMask : Object
    {
        // Fields 
        ParseObject _po = null;
        public ParseObject Po
        {
            get { return _po; }
            set { _po = value; }
        }

        // Properties 
        #region Properties
        private string _Title = string.Empty;
        public string Title
        {
            get
            {
                if (Po != null)
                {
                    _Title = Po.Get<string>("Title");
                }

                return _Title;
            }
            set { _Title = value; }
        }

        private string _Name = string.Empty;
        public string Name
        {
            get
            {
                if (Po != null)
                {
                    _Name = Po.Get<string>("Name");
                }

                return _Name;
            }
            set { _Name = value; }
        }

        private string _Category = string.Empty;
        public string Category
        {
            get
            {
                if (Po != null)
                {
                    try
                    {
                        _Category = Po.Get<string>("Category");
                    }
                    catch (KeyNotFoundException knfex)
                    {

                    }
                    catch (Exception ex)
                    {

                    }
                }

                return _Category;
            }
            set { _Category = value; }
        }

        private bool _IsChecked = false;
        public bool IsChecked
        {
            get
            {
                if (Po != null)
                {
                    try
                    {
                        _IsChecked = Po.Get<bool>("IsChecked");
                    }
                    catch (KeyNotFoundException knfex)
                    {

                    }
                    catch (Exception ex)
                    {

                    }
                }
                return _IsChecked;
            }
            set
            {
                _IsChecked = value;
                if (Po != null)
                {
                    try
                    {
                        Po["IsChecked"] = _IsChecked;

                        this.Update();
                    }
                    catch (KeyNotFoundException knfex)
                    {

                    }
                    catch (Exception ex)
                    {

                    }
                }
            }
        }

        private bool _IsStar = false;
        public bool IsStar
        {
            get
            {
                if (Po != null)
                {
                    try
                    {
                        _IsStar = Po.Get<bool>("IsStar");
                    }
                    catch (KeyNotFoundException knfex)
                    {

                    }
                    catch (Exception ex)
                    {

                    }
                }
                return _IsStar;
            }
            set
            {
                _IsStar = value;
                if (Po != null)
                {
                    try
                    {
                        Po["IsStar"] = _IsStar;

                        this.Update(false);
                    }
                    catch (KeyNotFoundException knfex)
                    {

                    }
                    catch (Exception ex)
                    {

                    }
                }
            }
        }
        #endregion

        // ctr 
        public ParseObjectMask(ParseObject po)
            : base()
        {
            this.Po = po;
        }

        // MEthods 
        public async void Update(bool withUpdate = true)
        {
            await ParseHelper.UpdateItemAsync(Po, ParseHelper.GetCurrentApp());

            if (withUpdate)
            {
                ParseCustomEventArgs args = new ParseCustomEventArgs("Items:Refresh");
                ParseOne.InformationSending(args);
            }
        }

        // Map Methods 
        public static ParseObjectMask Map(ParseObject po)
        {
            ParseObjectMask mask = new ParseObjectMask(po);
            return mask;
        }

        // Parse.com Methods 
        public static async Task<ParseObjectMask> CreateItemAsync(string title, string type, ParseApp app)
        {
            ParseObject po = await ParseHelper.CreateItemAsync(title, type, app);

            return ParseObjectMask.Map(po);
        }
        public static async Task<ParseObjectMask> UpdateItemAsync(ParseObjectMask item, ParseApp app)
        {
            ParseObject po = await ParseHelper.UpdateItemAsync(item.Po, app);

            return ParseObjectMask.Map(po);
        }
        public static async Task<string> DeleteItemAsync(ParseObjectMask item, ParseApp app)
        {
            string result = await ParseHelper.DeleteItemAsync(item.Po, app);

            return result;
        }

        // Assign // Remove 
        public static void AssignItemAsync(ParseObjectMask parent, ParseObjectMask child, ParseApp app)
        {
            ParseHelper.AssignItemAsync(parent.Po, child.Po, app);
        }
        public static void RemoveItemAsync(ParseObjectMask parent, ParseObjectMask child, ParseApp app)
        {
            ParseHelper.RemoveItemAsync(parent.Po, child.Po, app);
        }

        // Search // Find 
        public static async Task<List<ParseObjectMask>> SearchItemsByTypeAsync(string matchcode, string type, ParseApp app)
        {
            List<ParseObject> items = await ParseHelper.SearchItemsByTypeAsync(matchcode, type, app);

            List<ParseObjectMask> results = new List<ParseObjectMask>();

            foreach (ParseObject item in items)
            {
                ParseObjectMask newItem = ParseObjectMask.Map(item);
                results.Add(newItem);
            }

            return results;
        }

        // Related 
        public static async Task<List<ParseObjectMask>> GetRelatedItemsByTypeAsync(ParseObjectMask parent, string type, ParseApp app)
        {
            List<ParseObject> items = await ParseHelper.GetRelatedItemsByTypeAsync(parent.Po, type, app);

            List<ParseObjectMask> results = new List<ParseObjectMask>();

            foreach (ParseObject item in items)
            {
                ParseObjectMask newItem = ParseObjectMask.Map(item);
                results.Add(newItem);
            }

            return results;
        }

        // Files 
        public static async Task<ParseObjectMask> AttachFileToParent(ParseObjectMask parent, byte[] data, string filename, ParseApp app)
        {
            ParseObject pom = await ParseHelper.AttachFileToParent(parent.Po, data, filename, app);
            return ParseObjectMask.Map(pom);
        }
    }

    public static class ParseHelper
    {
        // App 
        private static ParseApp _CurrentApp = null;
        public static ParseApp GetCurrentApp()
        {
            return _CurrentApp;
        }
        public static void RegisterApp(ParseApp app)
        {
            if (app != null)
            {
                if (_CurrentApp == null)
                {
                    _CurrentApp = app;

                    // Parse 
                    ParseClient.Initialize(app.Key, app.Secret);
                }

                if (_CurrentApp.Key != app.Key)
                {
                    // Parse 
                    ParseClient.Initialize(app.Key, app.Secret);
                    _CurrentApp = app;
                }
            }
        }

        // Parse.com Methods 
        internal static async Task<ParseObject> CreateItemAsync(string title, string type, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Check for Title 
            if (title == string.Empty || type == string.Empty)
                return null;

            // Check for existing Item 
            ParseObject no = await GetItemByNameAsync(type, title, app);
            if (no != null)
                return no;

            var ctn = new ParseObject(type);
            ctn["Title"] = title;
            ctn["Type"] = type;
            ctn["Matchcode"] = title + " # " + type;
            await ctn.SaveAsync();

            await Task.Delay(1000);

            return await GetItemByIDAsync(type, ctn.ObjectId, app);
        }
        internal static async Task<ParseObject> UpdateItemAsync(ParseObject item, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            await item.SaveAsync();

            string type = item["Type"].ToString();
            string id = item.ObjectId;
            return await GetItemByIDAsync(type, id, app);
        }
        internal static async Task<string> DeleteItemAsync(ParseObject item, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            await item.DeleteAsync();

            return "";
        }

        // Single
        private static async Task<ParseObject> GetItemByIDAsync(string type, string id, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Query 
            ParseQuery<ParseObject> query = ParseObject.GetQuery(type);
            ParseObject item = await query.GetAsync(id);

            ParseObject ext = item as ParseObject;
            return ext;
        }
        private static async Task<ParseObject> GetItemByNameAsync(string type, string title, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Query 
            var query = from items in ParseObject.GetQuery(type)
                        where items.Get<string>("Title") == title
                        select items;

            IEnumerable<ParseObject> results = await query.FindAsync();
            return results.FirstOrDefault() as ParseObject;
        }
        private static async Task<ParseObject> GetFirstItemByTypeAsync(string type, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Query 
            ParseObject item = await ParseObject.GetQuery(type).FirstOrDefaultAsync() as ParseObject;
            return item;
        }

        // Get all 
        private static async Task<List<ParseObject>> GetItemsByTypeAsync(string type, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Query 
            IEnumerable<ParseObject> items = await ParseObject.GetQuery(type).FindAsync();
            return items.Cast<ParseObject>().ToList();
        }

        // Search // Find 
        internal static async Task<List<ParseObject>> SearchItemsByTypeAsync(string matchcode, string type, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Query 
            IEnumerable<ParseObject> items = await ParseObject.GetQuery(type).FindAsync();

            // Filter 
            if (matchcode != string.Empty)
            {
                List<ParseObject> results = new List<ParseObject>();

                foreach (ParseObject obj in items)
                {
                    string title = obj.Get<string>("Title");
                    if (title.ToLower().Contains(matchcode.ToLower()))
                    {
                        results.Add(obj);
                    }
                }

                return results;
            }
            else
            {
                List<ParseObject> results = new List<ParseObject>();

                foreach (ParseObject obj in items)
                {
                    ParseObject item = obj as ParseObject;
                    results.Add(item);
                }

                return results;
            }
        }

        // Related 
        internal static async Task<List<ParseObject>> GetRelatedItemsByTypeAsync(ParseObject parent, string type, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Query 
            ParseQuery<ParseObject> query = parent.GetRelation<ParseObject>(type).Query;
            IEnumerable<ParseObject> items = await query.FindAsync();

            List<ParseObject> _iems = items.ToList();
            return _iems;
        }

        // Assign // Remove 
        internal static async void AssignItemAsync(ParseObject parent, ParseObject child, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return;

            // Query 
            string _relationType = child["Type"].ToString();
            ParseRelation<ParseObject> relation = parent.GetRelation<ParseObject>(_relationType);
            relation.Add(child);
            await parent.SaveAsync();
        }
        internal static async void RemoveItemAsync(ParseObject parent, ParseObject child, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return;

            // Query 
            string _relationType = child["Type"].ToString();
            ParseRelation<ParseObject> relation = parent.GetRelation<ParseObject>(_relationType);
            relation.Remove(child);
            await parent.SaveAsync();
        }

        // Files 
        internal async static Task<ParseObject> AttachFileToParent(ParseObject parent, byte[] data, string filename, ParseApp app)
        {
            // Check for App Registration 
            if (app != null)
                ParseHelper.RegisterApp(app);
            else
                return null;

            // Check for Title 
            if (filename == string.Empty || data.Length == 0)
                return null;

            if (!File.Exists(filename))
                return null;

            FileInfo fi = new FileInfo(filename);

            // File 
            //ParseFile file = new ParseFile(filename, data);
            //await file.SaveAsync();

            // FileObject 
            ParseObject child = await CreateItemAsync(fi.Name, "File", app);
            //child["file"] = file;
            //await ParseHelper.UpdateItemAsync(child, app);

            // Association 
            ParseHelper.AssignItemAsync(parent, child, app);

            return child;
        }

        // Serialization 
        private static void CheckForAutoStartEntry(string path = "Microsoft\\Windows\\CurrentVersion\\Run")
        {
            try
            {
                string key = "Parse Client Tools";

                //string file = System.IO.Path.GetDirectoryName(
                //    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
                //file += "\\SERINO SearchSTER! for Windows.exe";

                string file = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;

                if (file.EndsWith("vshost.exe"))
                    file = file.Replace("vshost.", "");

                // Create Key 
                RegistryKey reg = Registry.CurrentUser;
                if (reg != null)
                {
                    RegistryKey software = reg.OpenSubKey("Software", true);
                    if (software != null)
                    {
                        RegistryKey child = software.OpenSubKey(path, true);
                        if (child.GetValue(key) == null)
                        {
                            child = software.CreateSubKey(path);
                            if (System.IO.File.Exists(file))
                            {


                                child.SetValue(key, file);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }

        // Serialization 
        public static string SerializeObject<T>(this T toSerialize)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());

            using (StringWriter textWriter = new StringWriter())
            {
                xmlSerializer.Serialize(textWriter, toSerialize);
                return textWriter.ToString();
            }
        }
        public static T DeserializeFromString<T>(this string objectData)
        {
            return (T)DeserializeFromString(objectData, typeof(T));
        }

        // Helper // Deserialization 
        private static object DeserializeFromString(this string objectData, Type type)
        {
            var serializer = new XmlSerializer(type);
            object result;

            using (TextReader reader = new StringReader(objectData))
            {
                result = serializer.Deserialize(reader);
            }

            return result;
        }
        private static string SplitByUpperCase(this string text)
        {
            var r = new Regex(@"
                (?<=[A-Z])(?=[A-Z][a-z]) |
                 (?<=[^A-Z])(?=[A-Z]) |
                 (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);

            return r.Replace(text, " ");
        }
    }

    public class ParseApp
    {
        public Guid GUID { get; set; }
        public string Name { get; set; }
        public string Key { get; set; }
        public string Secret { get; set; }
    }
    public class ParseClass
    {
        public Guid GUID { get; set; }
        public string Name { get; set; }

        // Relation 
        public Guid ParseAppGUID { get; set; }
    }
    public class ParseProperty
    {
        public Guid GUID { get; set; }
        public string Name { get; set; }

        // Relation 
        public Guid ParseClassGUID { get; set; }
    }

    public class FwGroupList
    {
        private string _Title = "Type";
        public string Title
        {
            get { return _Title; }
            set { _Title = value; }
        }

        private string _Property = "Type";
        public string Property
        {
            get { return _Property; }
            set { _Property = value; }
        }

        public static FwGroupList BuildItem(string title, string property)
        {
            FwGroupList group = new FwGroupList();
            group.Title = title;
            group.Property = property;
            return group;
        }

        public override string ToString()
        {
            return this.Title;
        }
    }
    public static class BindingHelper
    {
        // Binding 
        public static Binding BindPropertyThis(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, string bindingPath,
            IValueConverter converter, string converterParameter)
        {
            if (dependencyObject != null)
            {
                if (BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    BindingOperations.ClearBinding(dependencyObject, dependencyProperty);
                }

                if (!BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    Binding binding = new Binding(bindingPath);
                    binding.Converter = converter;
                    binding.ConverterParameter = converterParameter;
                    binding.Mode = BindingMode.TwoWay;
                    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                    BindingOperations.SetBinding(dependencyObject, dependencyProperty, binding);

                    return binding;
                }
            }
            return null;
        }
        private static Binding BindPropertyThis(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, string bindingPath)
        {
            if (dependencyObject != null)
            {
                if (BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    BindingOperations.ClearBinding(dependencyObject, dependencyProperty);
                }

                if (!BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    Binding binding = new Binding(bindingPath);
                    binding.Mode = BindingMode.TwoWay;
                    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                    BindingOperations.SetBinding(dependencyObject, dependencyProperty, binding);

                    return binding;
                }
            }
            return null;
        }

        private static Binding BindPropertyThis(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, string bindingPath, BindingMode mode)
        {
            if (dependencyObject != null)
            {
                if (BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    BindingOperations.ClearBinding(dependencyObject, dependencyProperty);
                }

                if (!BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    Binding binding = new Binding(bindingPath);
                    binding.Mode = mode;
                    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                    BindingOperations.SetBinding(dependencyObject, dependencyProperty, binding);

                    return binding;
                }
            }

            return null;
        }
        private static Binding BindPropertyThis(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, string bindingPath, IValueConverter converter, System.Windows.Controls.ValidationRule validationRule)
        {
            if (dependencyObject != null)
            {
                if (BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    BindingOperations.ClearBinding(dependencyObject, dependencyProperty);
                }

                if (!BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    Binding binding = new Binding(bindingPath);
                    binding.Converter = converter;
                    binding.Mode = BindingMode.TwoWay;
                    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

                    // Validation 
                    binding.ValidationRules.Add(validationRule);

                    BindingOperations.SetBinding(dependencyObject, dependencyProperty, binding);

                    return binding;
                }
            }
            return null;
        }
        private static Binding TemplateBinding(this DependencyObject dependencyObject, DependencyProperty dependencyProperty, string bindingPath)
        {
            if (dependencyObject != null)
            {
                if (BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    BindingOperations.ClearBinding(dependencyObject, dependencyProperty);
                }

                if (!BindingOperations.IsDataBound(dependencyObject, dependencyProperty))
                {
                    Binding binding = new Binding(bindingPath);
                    binding.Mode = BindingMode.TwoWay;
                    binding.RelativeSource = new RelativeSource(RelativeSourceMode.Self);
                    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                    BindingOperations.SetBinding(dependencyObject, dependencyProperty, binding);

                    return binding;
                }
            }
            return null;
        }

        public static void ValidateBinding(FrameworkElement fe, DependencyProperty dp)
        {
            try
            {
                BindingExpression binding = fe.GetBindingExpression(dp);
                if (binding != null)
                {
                    binding.UpdateSource();
                }
            }
            catch (Exception ex)
            {

            }
        }
    }
}
